use std::collections::HashMap;

use crate::{sparse_set::*, component::ComponentId};


/// 
/// 查询筛选类型
#[derive(Debug,Clone, Copy)]
pub enum FilterType {
    /// 必须存在
    With,
    /// 必须不存在
    Without,
    /// 可选
    Optional
}

///
/// 组件筛选器
/// 
#[derive(Debug,Clone, Copy)]
pub struct Filter {
    pub component_id:ComponentId,
    pub filter_type:FilterType
}

#[derive(Clone, Copy)]
struct FilterInfo<'a> {
    archetypes:Option<&'a SparseSet<usize>>,
    filter_type:FilterType,
    matching_archetype_len:usize,
    output_index:Option<usize>
}

///
/// 
#[derive(Clone,Debug)]
pub(crate) struct ArchetypeMatch<const CHANNEL_COUNT:usize> {
    pub archetype_index:usize,
    pub channels:[Option<usize>;CHANNEL_COUNT]
}

pub(crate) struct MatchingArchetypeIterator<'a , const CHANNEL_COUNT:usize> {
    offset:usize,
    filter_info:Vec<FilterInfo<'a>>,
    storage_info:&'a StorageLookup
}

impl<'a,const CHANNEL_COUNT:usize> Iterator for MatchingArchetypeIterator<'a,CHANNEL_COUNT> {
    type Item = ArchetypeMatch<CHANNEL_COUNT>;
    fn next(&mut self) -> Option<Self::Item> {
        fn match_tail_filters(
            filters:&[FilterInfo],
            archetype_index:usize,
            channels:&mut [Option<usize>]
        ) -> bool {
            for filter_info in filters.iter() {
                match filter_info.filter_type {
                    FilterType::With => {
                        if let Some(arthetypes) = filter_info.archetypes {
                            if let Some(channel) = arthetypes.get(archetype_index) {
                                if let Some(output_index) = filter_info.output_index {
                                    channels[output_index]=Some(*channel);
                                }
                                continue;
                            }
                        }
                        return false;
                    },
                    FilterType::Without => {
                        if filter_info.archetypes.map_or(false, |a|a.get(archetype_index).is_some()) {
                            return false
                        }
                    },
                    FilterType::Optional => {
                        if let Some(channel) = filter_info
                        .archetypes
                        .map(|a|a.get(archetype_index))
                        .flatten() {
                            if let Some(output_index) = filter_info.output_index {
                                channels[output_index] = Some(*channel);
                            }
                        }else if let Some(output_index) = filter_info.output_index {
                            channels[output_index] = None;
                        }
                    },
                }
            }
            true
        }
        

        let (first_filter_info,tail_filter_info) = self.filter_info
            .split_first()?;

        match first_filter_info.filter_type {
            FilterType::With => {
                let archetypes = first_filter_info.archetypes?;
                for (channel,&archetype_index) in archetypes.dense()[self.offset..]
                .iter()
                .zip(archetypes.dense_index_into_sparse_index()[self.offset..].iter()) {
                    self.offset += 1;
                    let mut channels = [None;CHANNEL_COUNT];
                    if let Some(output_index) = first_filter_info.output_index {
                        channels[output_index] = Some(*channel);
                    }

                    if match_tail_filters(tail_filter_info, archetype_index, &mut channels) {
                        return Some(ArchetypeMatch {
                            archetype_index,
                            channels,
                        })
                    }
                }

            },
            FilterType::Without | FilterType::Optional => {
                for archetype_index in &self.storage_info.all_archetypes[self.offset..] {
                    self.offset += 1;

                    let mut channels = [None; CHANNEL_COUNT];
                    if match_tail_filters(&self.filter_info, *archetype_index, &mut channels) {
                        return Some(ArchetypeMatch {
                            archetype_index: *archetype_index,
                            channels,
                        });
                    }

                }
            },
            
        }
        None
   
    }
}


#[derive(Default)]
pub(crate) struct StorageLookup {
    /// 组件 🆔 到 组件类型集合索引的映射
    component_archetype:HashMap<ComponentId,SparseSet<usize>>,
    /// 所有的组件类型索引的集合
    all_archetypes:Vec<usize>
}

impl StorageLookup {
    pub(crate) fn new_archetype(&mut self,archetype_index:usize,component_ids:&[ComponentId]) {
        component_ids.iter().enumerate()
            .for_each(|(i,cmp)|{
                self.component_archetype
                .entry(*cmp)
                .or_insert_with(SparseSet::default)
                .insert(archetype_index, i);
            });

        self.all_archetypes.push(archetype_index);
    }

    pub(crate) fn matching_archetype_iterator<const CHANNEL_COUNT:usize>(&self,filters:&[(Option<usize>,Filter)]) -> MatchingArchetypeIterator<CHANNEL_COUNT> {
        let mut filter_info = Vec::with_capacity(filters.len());

        for (output_index,filter) in filters.iter() {
            let archetypes = self.component_archetype.get(&filter.component_id);

            let matching_archetype_len = match filter.filter_type {
                FilterType::With => archetypes.map_or(0, |a|a.len()),
                FilterType::Without => self.all_archetypes.len() - archetypes.map_or(0, |a|a.len()),
                FilterType::Optional => self.all_archetypes.len(),
            };

            filter_info.push(FilterInfo {
                archetypes,
                filter_type: filter.filter_type,
                matching_archetype_len,
                output_index: *output_index,
            })

        }

        filter_info.sort_by_key(|info|info.matching_archetype_len);

        MatchingArchetypeIterator {
            offset:0,
            filter_info,
            storage_info:self
        }

    }
}




